﻿namespace Sai.Core.Reporting
{
	using System;
	using System.Collections.Generic;
	using System.IO;
	using Sai.Core.Sandcastle;

	/// <summary>
	/// Builder used to find and replace SDK topics in a sandcastle project.
	/// </summary>
	internal class ProjectBuilder
	{
		public event EventHandler BuildStarting;

		public event EventHandler PreBuildStarting;

		public event EventHandler PreBuildComplete;
		
		public event EventHandler PostBuildStarting;

		public event EventHandler PostBuildComplete;

		public event EventHandler<BuildErrorEventArgs> BuildError;

		public event EventHandler BuildComplete;

		public ProjectBuilder(IList<ReportIndex> reportOutput)
		{
			this.ReportOutput = new ReportIndexCollection(reportOutput);
		}

		public string SandcastleProject { get; set; }

		public string TopicId { get; set; }

		private string OutputFolder { get; set; }

		private ReportIndexCollection ReportOutput { get; set; }

		public void Build()
		{
			try
			{
				this.OnBuildStarting();

				this.ExecutePreBuildSteps();

				this.BuildInternal();

				this.ExecutePostBuildSteps();

				this.OnBuildComplete();
			}
			catch (ApplicationException ex)
			{
				this.OnError(ex);
			}
		}

		private void ExecutePreBuildSteps()
		{
			this.OnPreBuildStarting();

			this.CheckPreconditions();
			
			this.OnPreBuildComplete();
		}

		private void CheckPreconditions()
		{
			/* nothing */
			if (!string.IsNullOrEmpty(this.SandcastleProject))
			{
				if (!File.Exists(this.SandcastleProject))
				{
					string message = string.Format("Sandcastle project file does not exist at \"{0}\"", this.SandcastleProject);
					this.OnError(new FileNotFoundException(message));
				}
			}

			if (!string.IsNullOrEmpty(this.TopicId))
			{
				try
				{
					string attemptedGUid = new Guid(this.TopicId).ToString();
				}
				catch (FormatException)
				{
					this.OnError(new ArgumentException("Topic Id is not valid", "Topic Id"));
				}
			}
		}

		private void BuildInternal()
		{
			string baseFolder = this.SandcastleProject.ContainingFolder();
			string projectFilePath = this.SandcastleProject.AbsoluteFromFolder(baseFolder);

			this.BackupFile(projectFilePath);

			Project project = Project.LoadFrom(this.SandcastleProject);
			
			if (project == null)
			{
				// error ?
				return;
			}
			
			// need to find content layout from project.
			this.BackupFile(project.ContentLayoutPath);

			const bool OverwriteExisting = true;

			ReportIndexCollection newFiles = new ReportIndexCollection();

			// now copy each item to the new folder...
			string copyFolder = project.ContentLayoutName.AbsoluteFromFolder(baseFolder).ContainingFolder();

			foreach (var newReport in this.ReportOutput)
			{
				string fileName = Path.GetFileName(newReport.Path);
							
				string copyPath = Path.Combine(copyFolder, fileName);
				File.Copy(newReport.Path, copyPath, OverwriteExisting);
				
				newFiles.Add(new ReportIndex
				{
					Guid = newReport.Guid,
					Path = copyPath,
					Title = newReport.Title
				});
			}

			project.ReplaceContent(this.TopicId, newFiles);

			project.SaveTo(projectFilePath);
		}
		
		// temporarily backup files while we monkey with them.
		private void BackupFile(string fileName)
		{
			string backupName = fileName + ".bak";

			if (File.Exists(backupName))
			{
				File.Delete(backupName);
			}

			File.Copy(fileName, backupName);
		}

		private void ExecutePostBuildSteps()
		{
			this.OnPostBuildStarting();

			this.OnPostBuildComplete();
		}

		private void OnBuildStarting()
		{
			this.BuildStarting.Fire(this);
		}

		private void OnPreBuildStarting()
		{
			this.PreBuildStarting.Fire(this);
		}

		private void OnPreBuildComplete()
		{
			this.PreBuildComplete.Fire(this);
		}

		private void OnPostBuildStarting()
		{
			this.PostBuildStarting.Fire(this);
		}

		private void OnPostBuildComplete()
		{
			this.PostBuildComplete.Fire(this);
		}

		private void OnError(Exception ex)
		{
			this.BuildError.Fire(this, new BuildErrorEventArgs(ex));
		}

		private void OnBuildComplete()
		{
			this.BuildComplete.Fire(this);
		}
	}
}
